
#include "stdafx.h"
#include <windows.h>
#include <initguid.h>				// PKEY_AudioEngine_DeviceFormat
#include <mmdeviceapi.h>
#include <functiondiscoverykeys.h>  // PKEY_Device_FriendlyName
#include <audioclient.h>			// WAVEFORMATEX, WAVEFORMATEXTENSIBLE
#include <endpointvolume.h>


#define EXIT_ON_ERROR(hres)	if (FAILED(hres))	{ goto Exit; }
#define SAFE_RELEASE(punk)	if ((punk) != NULL)	{ (punk)->Release(); (punk) = NULL; }

#define	MAX_VOLUME					100
#define	DEF_CAPTURE_SAMPLESPERSEC	16000


void setVolume(float fRender, float fCapture)
{
	HRESULT hr;
	IMMDeviceEnumerator *pEnumerator = NULL;
	IMMDeviceCollection *pCollection = NULL;
	IMMDevice *pDevice = NULL;
	IPropertyStore *pProps = NULL;
	PROPVARIANT varName, varFormat;
	IMMEndpoint *pEndpoint = NULL;
	EDataFlow DataFlow;
	IAudioEndpointVolume *pVolume = NULL;
	UINT uCount, i;
	float fLevel;

	CoInitialize(NULL);

	hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (LPVOID *)&pEnumerator);
	EXIT_ON_ERROR(hr);

	// eRender, eCapture, eAll
	hr = pEnumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &pCollection);
	EXIT_ON_ERROR(hr);
	SAFE_RELEASE(pEnumerator);

	hr = pCollection->GetCount(&uCount);
	EXIT_ON_ERROR(hr);
	for (i=0; i<uCount; i++)
	{
		hr = pCollection->Item(i, &pDevice);
		EXIT_ON_ERROR(hr);

		hr = pDevice->OpenPropertyStore(STGM_READWRITE, &pProps);
		EXIT_ON_ERROR(hr);

		PropVariantInit(&varName);
		hr = pProps->GetValue(PKEY_Device_FriendlyName, &varName);
		EXIT_ON_ERROR(hr);
#ifdef	UNICODE
		if (NULL != _tcsstr(varName.pwszVal, USB_SOUND_STR))
#else
		{
			char szTextA[MAX_PATH];
			int nLen = WideCharToMultiByte(CP_ACP, 0, varName.pwszVal, -1, szTextA, MAX_PATH, NULL, NULL);
			szTextA[nLen] = NULL;
			if (NULL != _tcsstr(szTextA, USB_SOUND_STR))
				fLevel = 1.;
			else
				fLevel = 0.;
		}
		if (1. == fLevel)
#endif
		{
			hr = pDevice->QueryInterface(__uuidof(IMMEndpoint), (void **)&pEndpoint);
			EXIT_ON_ERROR(hr);

			hr = pEndpoint->GetDataFlow(&DataFlow);
			EXIT_ON_ERROR(hr);
			if (eRender == DataFlow)
				fLevel = fRender;
			else if (eCapture == DataFlow)
				fLevel = fCapture;

			// +++ Format Change +++
			if (eCapture == DataFlow)
			{
				PropVariantInit(&varFormat);
				hr = pProps->GetValue(PKEY_AudioEngine_DeviceFormat, &varFormat);
				EXIT_ON_ERROR(hr);

				if ((VT_BLOB == varFormat.vt) && (varFormat.blob.cbSize > 0))
				{
					WAVEFORMATEX *pWaveFormatExBlob = (WAVEFORMATEX *)(varFormat.blob.pBlobData);
					WAVEFORMATEXTENSIBLE *pWaveFormatExtensibleBlob = (WAVEFORMATEXTENSIBLE *)pWaveFormatExBlob;

					if (pWaveFormatExBlob->nSamplesPerSec != DEF_CAPTURE_SAMPLESPERSEC)
					{
						pWaveFormatExBlob->nChannels = 2;
						pWaveFormatExBlob->nSamplesPerSec = DEF_CAPTURE_SAMPLESPERSEC;
						pWaveFormatExBlob->nBlockAlign = pWaveFormatExBlob->nChannels * pWaveFormatExBlob->wBitsPerSample / 8;
						pWaveFormatExBlob->nAvgBytesPerSec = pWaveFormatExBlob->nSamplesPerSec * pWaveFormatExBlob->nBlockAlign;
						pWaveFormatExtensibleBlob->Samples.wValidBitsPerSample = pWaveFormatExBlob->wBitsPerSample;

						//this works when I run this code with administrative privileges; otherwise I get access denied error
						hr = pProps->SetValue(PKEY_AudioEngine_DeviceFormat, varFormat);
						EXIT_ON_ERROR(hr);

						hr = pProps->Commit();
						EXIT_ON_ERROR(hr);
					}
				}
			}
			// --- Format Change ---

			// +++ Volume Change +++
			{
				hr = pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (LPVOID *)&pVolume);
				EXIT_ON_ERROR(hr);

				hr = pVolume->SetMasterVolumeLevelScalar((fLevel / MAX_VOLUME), NULL);
				EXIT_ON_ERROR(hr);
			}
			// --- Volume Change ---
		}
		PropVariantClear(&varFormat);
		PropVariantClear(&varName);
		SAFE_RELEASE(pVolume);
		SAFE_RELEASE(pEndpoint);
		SAFE_RELEASE(pProps);
		SAFE_RELEASE(pDevice);
	}

Exit:
	SAFE_RELEASE(pVolume);
	SAFE_RELEASE(pEndpoint);
	SAFE_RELEASE(pProps);
	SAFE_RELEASE(pDevice);
	SAFE_RELEASE(pCollection);
	SAFE_RELEASE(pEnumerator);

	CoUninitialize();
}
